#include "ArmyState.h"
#include "UserAPI.h"
#include "BasicAI.h"
#include "params.h"

using namespace ArmyStateNS;

map<OffensiveBattleState::PositionIndex, int> OffensiveBattleState::city_hardness;

#define hardness(city) city_hardness[PositionIndex(api.GetCityCenter(city))]

void OffensiveBattleState::execute()
{
	/*
		The general behaves like following:

		If I am in in a battle, which means I can see enemies within range ATTACKING_RANGE
			If enemies are extremely strong, then try to find a safe place (#1)
			Otherwise, if enemies are strong, then try to find another target (#2)
			Otherwise, if I can attack some enemy, go to fuck them, (#3)
			Otherwise, all the weak enemies hide in cities, 
				if I am ranger-troop, go to fuck the enemies, (#3)
				(*) otherwise, if enemies are extremely weak and I can destroy the city in 3 rounds, try to attack the city (#4)
				(*) otherwise, try to find another target (#2)
		otherwise, region clear, I am safe
			if there's some enemy/empty city around which can be taken in 5 rounds, go to take it. (#4)
			otherwise, find another target (#2)

		(#1):	extremely enemies around! withdraw!
			if I can reach some city for recuiting, go ahead, 
			otherwise, withdraw

		(#2):	no targets around, and strong enemies around
			if I can find an empy city, go ahead (bypass enemy's sight)
			otherwise, if I can find a weakest enemy's city, where HP * 10 + enemy_around < my_army, go ahead (bypass enemy's sight)
			otherwise, withdraw

		(#3):	fuck weak enemies
			turns into temporary attacking enemy state

		(#4):	take a city
			if the city's HP > 0 and I can take it in 5 rounds, turns into temporary attakcing city state
			otherwise, goes to (#3)
	 */

	BasicAI *ai = BasicAI::instance();
	UserAPI &api = ai->get_api();
	Army *army = api.GetCntArmy();

	//if (m_target == Position(-1, -1))
	//{
	//	LOG << "Looking for the next target" << endl;
	//	int lowest_hardness = INFINITY;
	//	LandCity *selected_city = 0;
	//	for (int i = 0; i < (int) api.all_cities.size(); ++i)
	//	{
	//		LandCity *city = api.all_cities[i];
	//		if (city->owner == api.get_my_id())
	//			hardness(city) = 0;
	//		else
	//		{
	//			if (hardness(city) < lowest_hardness)
	//			{
	//				lowest_hardness = hardness(city);
	//				selected_city = city;
	//			}
	//		}
	//	}
	//	if (selected_city)
	//		m_target = api.GetCityCenter(selected_city);
	//	else
	//	{
	//		LOG << "no enemy city at all?!" << endl;
	//		//	TODO	try to attack enemy army
	//		enter_state(m_name, this, new IdleState(m_name));
	//		return;
	//	}

	//	LOG << "OffensiveState: General " << api.get_my_id() << " choose to attack city at " << toString(m_target) << endl;
	//}

	int attacking_available_range = params::get<int>("ATTACKING_AVAILABLE_RANGE", 8);
	if (api.InDanger(army->pos, attacking_available_range))
	{
		vector<Army*> op_troop = api.GetOpArmyAround(army->pos);
		vector<Army*> my_troop = api.GetMyArmyAround(army->pos);
		int op_count = api.GetArmyCount(op_troop);
		int my_count = api.GetArmyCount(my_troop);

		if (op_count > my_count * 1.5)
		{
			//	following strategy is not used.
			//	don't go to a fast fight, if we have a city in range 7, then go to defense the city
			//	if we have a city, the way to which is apart from the strong enemies, then go to that city
			//	if we can find a safe place, go ahead,
			//	otherwise, simply find the way to my city with strongest army.

			//LandCity *nearest_my_city = api.GetNearestMyCity(army->pos);
			//if (nearest_my_city && api.HamDistance(army->pos, api.GetCityCenter(nearest_my_city)) <= 7 && nearest_my_city->HP > 0)
			//{
			//	enter_state(m_name, this, new DefensiveBattleState(m_name, api.GetCityCenter(nearest_my_city)));
			//	return;
			//}

			//nearest_my_city = 0;
			//int best_cost = INFINITY;
			//for (int i = 0; i < (int) api.all_cities.size(); ++i)
			//{
			//	LandCity *city = api.all_cities[i];
			//	if (city->owner != api.get_my_id()) continue;
			//	bool safe = true;
			//	Position center = api.GetCityCenter(city);
			//	for (int j = 0; j < (int)op_troop.size(); ++j)
			//		if (api.HamDistance(op_troop[j]->pos, center) < api.HamDistance(army->pos, center))
			//			safe = false;
			//	if (safe && api.HamDistance(army->pos, center) < best_cost)
			//	{
			//		best_cost = api.HamDistance(army->pos, center);
			//		nearest_my_city = city;
			//	}
			//}

			//if (best_cost < INFINITY)
			//{
			//	//	withdraw to "nearest_my_city"
			//	enter_state(m_name, this, new IdleState(m_name));
			//	return;
			//}

			////	withdraw to a safe place
			//enter_state(m_name, this, new IdleState(m_name));
			//return;

			enter_state(m_name, this, new WithdrawState(m_name));
			return;
		} else if (op_count > my_count)
		{
			find_target();
			return;
		} else
		{
			LOG << "We have a strong troop than they have! go to fuck them" << endl;
			Army *enemy = api.GetNearestArmy(army->pos, op_troop);
			LOG_ASSERT(enemy, "enemy should not be empty!");

			//	do remember to call other armies of mine
			for (int i = 0; i < (int) my_troop.size(); ++i)
			{
				Army *fellow = my_troop[i];
				if (fellow->general->type == m_name) continue;
				GeneralType fellow_name = fellow->general->type;
				require_action(fellow_name, new TemporalAttackingState(fellow_name, ai->get_army_state(fellow_name), enemy->general->type));
			}

			//	attack by myself
			enter_state(m_name, this, new TemporalAttackingState(m_name, this, enemy->general->type));
			return;
		}
	} else
	{
		//	region clear!
		LandCity *best_city = NULL;
		for (int i = 0; i < (int) api.all_cities.size(); ++i)
		{
			LandCity *city = api.all_cities[i];
			Position center = api.GetCityCenter(city);
			if (city->owner == api.get_my_id()) continue;
			if (api.HamDistance(center, army->pos) > attacking_available_range + 1) continue;
			int required_round = params::get<int>("MAX_ROUND_TO_ATTACK_CITY", 5);
			if (army->numSoldier * 0.03 * required_round >= city->HP)
				if (best_city == NULL || best_city->HP > city->HP)
					best_city = city;
		}

		if (best_city)
		{
			//enter_state(m_name, this, new TemporalAttackingState(m_name, this, api.GetCityCenter(best_city)));
			if (best_city->HP > 0)
				api.AttackCity(ai->get_action_queue(), army->pos, best_city);
			else
				api.Move(ai->get_action_queue(), army->pos, api.GetCityCenter(best_city));
			return;
		} else
		{
			find_target();
			return;
		}
	}
}

void OffensiveBattleState::find_target()
{
	BasicAI *ai = BasicAI::instance();
	UserAPI &api = ai->get_api();
	Army *army = api.GetCntArmy();

	LandCity *best_city = NULL;
	int weakest_power = INFINITY;
	int the_delta = 0;
	for (int i = 0; i < (int) api.all_cities.size(); ++i)
	{
		LandCity *city = api.all_cities[i];
		Position center = api.GetCityCenter(city);

		if (city->owner == api.get_my_id()) continue;

		vector<Army*> op_troop = api.GetOpArmyAround(center);
		int op_count = api.GetArmyCount(op_troop);
		
		int power = city->HP * 10 + op_count;
		int delta = (int) op_troop.size() * 500;
		vector<ActionBase*> ret;
		if (power + delta < weakest_power + the_delta && api.FindSafeWayToRegion(ret, army->pos, center, army->numSoldier))
		{
			weakest_power = power;
			the_delta = delta;
			best_city = city;
		}
	}

	if (weakest_power < army->numSoldier)
	{
		api.FindSafeWayToRegion(ai->get_action_queue(), army->pos, api.GetCityCenter(best_city), army->numSoldier);
		return;
	} else
	{
		enter_state(m_name, this, new WithdrawState(m_name));
		return;
	}
}